//
//  PSConfirmationTest.m
//  PrefCore
//
//  Created by Sergey on 2/3/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "PSConfirmation.h"

@interface PSConfirmationTests : XCTestCase {
    PSGameSettings *settings;
    PSBid *bidSixNt;
    PSGame *aGame;
}

@end

@interface TestConfirmGame : PSGame

@property (nonatomic, readwrite) PSTrickCount tricks;

@property (nonatomic, readwrite) PSBid *bidContract;

-(instancetype)initWithTrickCount:(PSTrickCount)tricks andContract:(PSBid*)bidContract;

-(PSTrickCount)estimateHumanTrickCountWithSettings:(PSGameSettings*)settings;

-(PSBid*)estimateHumanContract;

@end

@implementation PSConfirmationTests

-(void)setUp {
    settings = [PSGameSettings settingsFromUserDefaults:[NSUserDefaults standardUserDefaults]];
    bidSixNt = [PSBid bidPlayWithTrump:kSuitNone tricks:6];
    aGame = [[PSGame alloc] init];
}

-(void)testConfirmAlways {
    XCTAssertTrue(PSConfirmContract(kConfirmationAlways, aGame, settings, bidSixNt));
    XCTAssertTrue(PSConfirmPlay(kConfirmationAlways, aGame, settings, bidSixNt));
    XCTAssertTrue(PSConfirmMisere(kConfirmationAlways, aGame, settings, bidSixNt));
    XCTAssertTrue(PSConfirmPass(kConfirmationAlways, aGame, settings, bidSixNt));
    XCTAssertTrue(PSConfirmWithoutThree(kConfirmationAlways, aGame, settings, bidSixNt));
}

-(void)testConfirmNever {
    XCTAssertFalse(PSConfirmContract(kConfirmationNever, aGame, settings, bidSixNt));
    XCTAssertFalse(PSConfirmPlay(kConfirmationNever, aGame, settings, bidSixNt));
    XCTAssertFalse(PSConfirmMisere(kConfirmationNever, aGame, settings, bidSixNt));
    XCTAssertFalse(PSConfirmPass(kConfirmationNever, aGame, settings, bidSixNt));
    XCTAssertFalse(PSConfirmWithoutThree(kConfirmationNever, aGame, settings, bidSixNt));
}


- (void)testConfirmPlayIntelligent {
    for (PSTrickCount trickEstimate = 0 ; trickEstimate <= PS_BID_MAX ; trickEstimate++) {
        TestConfirmGame *game = [[TestConfirmGame alloc] initWithTrickCount:trickEstimate andContract:nil];
        PSBid *bid = [PSBid bidPlayWithTrump:kSuitNone tricks:6];
        BOOL res = PSConfirmPlay(kConfirmationIntelligent, game, settings, bid);
        if (trickEstimate < 4) {
            XCTAssertTrue(res, "6 on %d should confirm", trickEstimate);
        } else {
            XCTAssertFalse(res, "6 on %d should not confirm", trickEstimate);
        }
        bid = [PSBid bidPlayWithTrump:kSuitNone tricks:7];
        res = PSConfirmPlay(kConfirmationIntelligent, game, settings, bid);
        if (trickEstimate < 6) {
            XCTAssertTrue(res, "7 on %d should confirm", trickEstimate);
        } else {
            XCTAssertFalse(res, "7 on %d should not confirm", trickEstimate);
        }
        bid = [PSBid bidPlayWithTrump:kSuitNone tricks:8];
        res = PSConfirmPlay(kConfirmationIntelligent, game, settings, bid);
        if (trickEstimate < 8) {
            XCTAssertTrue(res, "8 on %d should confirm", trickEstimate);
        } else {
            XCTAssertFalse(res, "8 on %d should not confirm", trickEstimate);
        }
        XCTAssertTrue(
            PSConfirmPlay(
                kConfirmationIntelligent
            ,   game
            ,   settings
            ,   [PSBid bidPlayWithTrump:kSuitNone tricks:9]
            )
        ,   @"Nine trick games are always confirmed"
        );
        XCTAssertTrue(
            PSConfirmPlay(
                kConfirmationIntelligent
            ,   game
            ,   settings
            ,   [PSBid bidPlayWithTrump:kSuitNone tricks:10]
            )
        ,   @"Ten trick games are always confirmed"
        );
    }
}

- (void)testConfirmPassIntelligent {
    PSBid *bid = [PSBid bidPass];
    for (PSTrickCount trickEstimate = 0 ; trickEstimate <= PS_BID_MAX ; trickEstimate++) {
        TestConfirmGame *game = [[TestConfirmGame alloc] initWithTrickCount:trickEstimate andContract:nil];
        game.options.bid = [PSBid bidPlayWithTrump:kSuitNone tricks:6];
        BOOL res = PSConfirmPass(kConfirmationIntelligent, game, settings, bid);
        if (trickEstimate >= 4) {
            XCTAssertTrue(res, "pass on %d should confirm", trickEstimate);
        } else {
            XCTAssertFalse(res, "pass on %d should not confirm", trickEstimate);
        }
        game.options.bid = [PSBid bidPlayWithTrump:kSuitNone tricks:7];
        res = PSConfirmPass(kConfirmationIntelligent, game, settings, bid);
        if (trickEstimate >= 6) {
            XCTAssertTrue(res, "pass on %d should confirm", trickEstimate);
        } else {
            XCTAssertFalse(res, "pass on %d should not confirm", trickEstimate);
        }
        game.options.bid = [PSBid bidPlayWithTrump:kSuitNone tricks:8];
        res = PSConfirmPass(kConfirmationIntelligent, game, settings, bid);
        if (trickEstimate >= 8) {
            XCTAssertTrue(res, "pass on %d should confirm", trickEstimate);
        } else {
            XCTAssertFalse(res, "pass on %d should not confirm", trickEstimate);
        }
        game.options.bid = [PSBid bidPlayWithTrump:kSuitNone tricks:9];
        XCTAssertTrue(
            PSConfirmPass(kConfirmationIntelligent, game, settings, bid)
        ,   @"Nine trick games are always confirmed"
        );
        game.options.bid = [PSBid bidPlayWithTrump:kSuitNone tricks:10];
        XCTAssertTrue(
            PSConfirmPass(kConfirmationIntelligent, game, settings, bid)
        ,   @"Ten trick games are always confirmed"
        );
    }
}

- (void)testConfirmMisereIntelligent {
    for (PSTrickCount trickEstimate = 0 ; trickEstimate <= PS_BID_MAX ; trickEstimate++) {
        TestConfirmGame *game = [[TestConfirmGame alloc] initWithTrickCount:trickEstimate andContract:nil];
        BOOL res = PSConfirmMisere(kConfirmationIntelligent, game, settings, [PSBid bidMisere]);
        if (trickEstimate) {
            XCTAssertTrue(res, @"Misere on non-zero estimate needs confirmation.");
        } else {
            XCTAssertFalse(res, @"Misere on zero estimate does not need confirmation.");
        }
    }
}

- (void)testConfirmWithoutThreeIntelligent {
    for (PSTrickCount trickEstimate = 0 ; trickEstimate <= PS_BID_MAX ; trickEstimate++) {
        PSBid *bidEstimate = (trickEstimate >= PS_BID_MIN)
            ? [PSBid bidPlayWithTrump:kSuitNone tricks:trickEstimate]
            : [PSBid bidPass];
        TestConfirmGame *game = [[TestConfirmGame alloc] initWithTrickCount:0 andContract:bidEstimate];
        for (PSTrickCount trickMin = PS_BID_MIN ; trickMin <= PS_BID_MAX ; trickMin++) {
            game.options.bid = [PSBid bidPlayWithTrump:kSuitNone tricks:trickMin];
            BOOL res = PSConfirmWithoutThree(kConfirmationIntelligent, game, settings, [PSBid bidWithoutThree]);
            if (trickEstimate >= PS_BID_MIN && trickMin > trickEstimate+1) {
                XCTAssertTrue(res, @"Without 3 on %d with %d estimate needs confirmation.", trickMin, trickEstimate);
            } else {
                XCTAssertFalse(res, @"Without 3 on %d with %d estimate does not need confirmation.", trickMin, trickEstimate);
            }
        }
    }
}

- (void)testConfirmContractIntelligent {
    PSCardSuit suits[] = {kSuitSpades, kSuitClubs, kSuitDiamonds, kSuitHearts, kSuitNone};
    for (PSTrickCount trickEstimate = PS_BID_MIN ; trickEstimate <= PS_BID_MAX ; trickEstimate++) {
        for (int i = 0 ; i != 5 ; i++) {
            PSBid *bidEstimate = [PSBid bidPlayWithTrump:suits[i] tricks:trickEstimate];
            TestConfirmGame *game = [[TestConfirmGame alloc] initWithTrickCount:0 andContract:bidEstimate];
            for (PSTrickCount trickUser = PS_BID_MIN ; trickUser <= PS_BID_MAX ; trickUser++) {
                for (int j = 0 ; j != 5 ; j++) {
                    PSBid *bidUser = [PSBid bidPlayWithTrump:suits[j] tricks:trickUser];
                    BOOL res = PSConfirmContract(kConfirmationIntelligent, game, settings, bidUser);
                    if (bidEstimate != bidUser) {
                        XCTAssertTrue(res, @"Expected a confirmation for %@ on %@", bidUser, bidEstimate);
                    } else {
                        XCTAssertFalse(res, @"Expected no confirmation on %@ when the bid matches the estimate.", bidUser);
                    }
                }
            }
        }
    }
}

@end

@implementation TestConfirmGame

-(instancetype)initWithTrickCount:(PSTrickCount)tricks andContract:(PSBid*)bidContract {
    if (self = [super init]) {
        _tricks = tricks;
        _bidContract = bidContract;
    }
    return self;
}

-(PSTrickCount)estimateHumanTrickCountWithSettings:(PSGameSettings*)settings {
    return _tricks;
}

-(PSBid*)estimateHumanContract {
    return _bidContract;
}

@end